package com.sinosoft.hydra.um.services;

import cn.com.sinux.spring.exception.BusinessException;
import cn.com.sinux.spring.utils.*;
import com.sinosoft.hydra.um.condition.SearchPBomCondition;
import com.sinosoft.hydra.um.constant.BaseConstant;
import com.sinosoft.hydra.um.domain.permission.TPBom;
import com.sinosoft.hydra.um.domain.permission.TPBomRelation;
import com.sinosoft.hydra.um.repositories.TPBomDAO;
import com.sinosoft.hydra.um.repositories.TPBomRelationDAO;
import com.sinosoft.hydra.um.services.component.dataimport.ImportParam;
import com.sinosoft.hydra.um.services.component.dataoutput.DataWriter;
import com.sinosoft.hydra.um.services.component.dataoutput.ExcelDataWriter;
import com.sinosoft.hydra.um.services.component.dataoutput.RowTemplate;
import com.sinosoft.hydra.um.services.component.dataoutput.parsers.ColumnParser;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.WritableCell;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.*;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Administrator on 2016/6/28.
 */
@Service
@Transactional
public class SysPBomService {

    @Autowired
    private TPBomDAO tpBomDAO;
    @Autowired
    private TPBomRelationDAO tpBomRelationDAO;

    /**
     * 导入PBOM数据信息
     *
     * @param importParam
     */
    public void doImportPBom(ImportParam importParam) {
        //读取数据流
        List<ImportParam.Source> sources = importParam.getSources();
//        取出现有的数据
        List<TPBom> tpBoms = new ArrayList<TPBom>(sources.size());
        for (ImportParam.Source source : sources) {
            tpBoms.add((TPBom) source.getObject());
        }
        if (ValidatorUtils.isArrayEmpty(tpBoms)) {
            return;
        }
        //根据节点倒叙装配成树
        for (int i = tpBoms.size() - 1; i >= 0; i--) {
            TPBom current = tpBoms.get(i);
            int dest = current.getNodeLevel() - 1;
            if (i > 0) {
                boolean find = false;
                //找到最近一个level比他少1的节点，将其定位父级节点
                for (int j = i - 1; j >= 0; j--) {
                    //
                    if (dest == tpBoms.get(j).getNodeLevel()) {
                        //找到该节点后将当前节点设为其子节点
                        TPBom parent = tpBoms.get(j);
                        parent.addChild(current);
                        find = true;
                        break;
                    }
                }
                if (!find) {
                    throw new BusinessException("导入的数据中存在没有父级节点的数据，请检查数据后重新导入");
                }
            }
        }
        //第一个为根节点
        TPBom root = tpBoms.get(0);
        //数据验证
        if (!Integer.valueOf(1).equals(root.getNodeLevel())) {
            throw new BusinessException("导入的数据中没有找到层级为1的根节点，请重新整理数据导入");
        }
        TPBom _root = new TPBom();
        BeanUtils.copyProperties(root, _root);
        _root.setTpBomRelations(null);
        _root.setStatus(BaseConstant.BomStatus.UN_PUBLISH.name());
        tpBomDAO.save(_root);
        root.setId(_root.getId());
        savePBom(root, false);
    }

    /**
     * 导出PBOM结构
     *
     * @param id
     */
    public byte[] doExportPBom(String id, final boolean hasCode) {
        final TPBom tpBom = findOne(id);
        if (tpBom == null) {
            throw new BusinessException("不存在的结构，导出失败");
        }
        tpBom.setNodeLevel(1);
        List<TPBom> tpBoms = reset(tpBom);
        RowTemplate<TPBom> rowTemplate = new RowTemplate<TPBom>();
        rowTemplate.addColumnTemplate(new RowTemplate.ColumnTemplate<TPBom>("设计结构编号", new ColumnParser<TPBom>() {

            public WritableCell parse(int row, int cellNum, TPBom tpBom) {
                return new Label(cellNum, row, hasCode ? tpBom.getCode() : "");
            }
        })).addColumnTemplate(new RowTemplate.ColumnTemplate<TPBom>("名称", new ColumnParser<TPBom>() {

            public WritableCell parse(int row, int cellNum, TPBom tpBom) {
                return new Label(cellNum, row, tpBom.getpName());
            }
        })).addColumnTemplate(new RowTemplate.ColumnTemplate<TPBom>("层次", new ColumnParser<TPBom>() {

            public WritableCell parse(int row, int cellNum, TPBom tpBom) {
                return new Number(cellNum, row, tpBom.getNodeLevel());
            }
        })).addColumnTemplate(new RowTemplate.ColumnTemplate<TPBom>("数量（件）", new ColumnParser<TPBom>() {

            public WritableCell parse(int row, int cellNum, TPBom tpBom) {
                return new Number(cellNum, row, tpBom.getNumber() == null ? 1 : tpBom.getNumber());
            }
        })).addColumnTemplate(new RowTemplate.ColumnTemplate<TPBom>("制造分类", new ColumnParser<TPBom>() {

            public WritableCell parse(int row, int cellNum, TPBom tpBom) {
                return new Label(cellNum, row, tpBom.getBomType());
            }
        })).addColumnTemplate(new RowTemplate.ColumnTemplate<TPBom>("备注", new ColumnParser<TPBom>() {

            public WritableCell parse(int row, int cellNum, TPBom tpBom) {
                return new Label(cellNum, row, tpBom.getRemark());
            }
        }));
        DataWriter<TPBom> tpBomDataWriter = new ExcelDataWriter<TPBom>(rowTemplate);
        try {
            return tpBomDataWriter.write(tpBoms);
        } catch (Exception e) {
            throw new BusinessException("导出数据失败");
        }
    }

    /**
     * 找到PBom的根节点
     *
     * @param tpBom
     * @return
     */
    public List<TPBom> findTop(final TPBom tpBom) {
        //根据某一个叶子节点获取顶层节点
        List<TPBom> top = new ArrayList<TPBom>();
        Specification<TPBomRelation> tpBomRelationSpecification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TPBomRelation>() {

            public void fillParam(Root<TPBomRelation> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("child").get("id"), tpBom.getId()));
            }
        });
        List<TPBomRelation> teBoms = tpBomRelationDAO.findAll(tpBomRelationSpecification);
        if (teBoms.isEmpty()) {
            top.add(tpBom);
        } else {
            for (TPBomRelation teBomRelation : teBoms) {
                top.addAll(findTop(teBomRelation.getRef()));
            }
        }
        return top;
    }

    /**
     * 删除PBOM
     *
     * @param id
     */
    public TPBom delPBom(String id) {
        TPBom tpBom = tpBomDAO.findOne(id);
        if (BaseConstant.BomStatus.DESTROY.name().equals(tpBom.getStatus())) {
            return tpBom;
        }
        if (BaseConstant.BomStatus.PUBLISH.name().equals(tpBom.getStatus())) {
            tpBom.setStatus(BaseConstant.BomStatus.DESTROY.name());
            tpBomDAO.save(tpBom);
        } else {
            //先解除原来pbom的关系
            unBindRelations(tpBom);
            tpBomDAO.delete(tpBom);
        }
        return tpBom;
    }

    private void unBindRelations(final TPBom tpBom) {
        Specification<TPBomRelation> relationSpecification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TPBomRelation>() {

            public void fillParam(Root<TPBomRelation> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.or(cb.equal(root.get("child").get("id").as(String.class), tpBom.getId()), cb.equal(root.get("ref").get("id").as(String.class), tpBom.getId())));
            }
        });
        List<TPBomRelation> relations = tpBomRelationDAO.findAll(relationSpecification);
        tpBomRelationDAO.delete(relations);
    }
//
//    private boolean validEquals(List<TPBom> tpBoms, List<TPBom> tpBomsB) {
//        if (ValidatorUtils.isArrayEmpty(tpBoms) && ValidatorUtils.isArrayEmpty(tpBomsB)) {
//            return true;
//        }
//        if (ValidatorUtils.isArrayEmpty(tpBoms) || ValidatorUtils.isArrayEmpty(tpBomsB) || tpBoms.size() != tpBomsB.size()) {
//            return false;
//        }
//        //对比编码
//        Map<String, Integer> stringIntegerMap = buildNumberMap(tpBoms);
//        Map<String, Integer> stringIntegerMapB = buildNumberMap(tpBomsB);
//        for (Map.Entry<String, Integer> entry : stringIntegerMap.entrySet()) {
//            Integer v1 = entry.getValue();
//            Integer v2 = stringIntegerMapB.get(entry.getKey());
//            if (!v1.equals(v2)) {
//                return false;
//            }
//        }
//        return true;
//    }

    private Map<String, Integer> buildNumberMap(List<TPBom> tpBoms) {
        Map<String, Integer> stringIntegerMap = new HashMap<String, Integer>();
        for (TPBom tpBom : tpBoms) {
            stringIntegerMap.put(tpBom.getCode(), tpBom.getNumber());
        }
        return stringIntegerMap;
    }

    /**
     * 保存顶层的pbom节点
     *
     * @param tpBom
     */
    public TPBom addTop(TPBom tpBom) {
        //检查code是否存在
        TPBom db = findByCode(tpBom.getCode());
        if (db != null) {
            throw new BusinessException("保存的结构编码已经存在，不能保存");
        }
        //递归遍历获取传入的孩子节点
        tpBom.setStatus(BaseConstant.BomStatus.UN_PUBLISH.name());
        return tpBomDAO.save(tpBom);
    }

    public List<TPBom> resetAnfFilterPublished(TPBom tpBom) {
        List<TPBom> tpBoms = new ArrayList<TPBom>();
        TPBom _pbom = findByCode(tpBom.getCode());
        if (_pbom == null) {
            _pbom = tpBom;
            _pbom.setId(null);
        } else if (BaseConstant.BomStatus.UN_PUBLISH.name().equals(_pbom.getStatus())) {
            tpBom.setId(_pbom.getId());
            _pbom = tpBom;
        }
        if (StringUtils.isBlank(_pbom.getStatus())) {
            _pbom.setStatus(BaseConstant.BomStatus.UN_PUBLISH.name());
        }
        if (BaseConstant.BomStatus.UN_PUBLISH.name().equals(_pbom.getStatus())) {
            tpBoms.add(tpBom);
            List<TPBom> children = tpBom.getChildren();
            if (ValidatorUtils.isArrayNotEmpty(children)) {
                for (TPBom child : children) {
                    tpBoms.addAll(resetAnfFilterPublished(child));
                }
            }
        }
        return tpBoms;
    }

    /**
     * 保存一个节点及其下面的所有孩子节点
     *
     * @param tpBom
     */
    public List<TPBom> savePBom(TPBom tpBom, boolean publish) {
        if (tpBom == null || findOne(tpBom.getId()) == null) {
            throw new BusinessException("保存的资源不存在");
        }

//        递归遍历获取传入的孩子节点  找到下属的孩子节点
        List<TPBom> all = resetAnfFilterPublished(tpBom);
        //检测是否有被发布的节点被修改了
        List<TPBomRelation> tpBomRelations = new ArrayList<TPBomRelation>();
        for (TPBom pa : all) {
            //查询所有节点，检查是否存在已发布的节点被修改的情况
            if (StringUtils.isBlank(pa.getStatus())) {
                pa.setStatus(BaseConstant.BomStatus.UN_PUBLISH.name());
            }
            if (pa.getNumber() == null) {
                tpBom.setNumber(1);
            }
            //设置ref
            List<TPBomRelation> relations = pa.getTpBomRelations();
            if (ValidatorUtils.isArrayNotEmpty(relations)) {
                for (TPBomRelation tpBomRelation : relations) {
                    tpBomRelation.setRef(pa);
                }
                tpBomRelations.addAll(relations);
            }
            pa.setTpBomRelations(null);
            if (publish) {
                pa.setStatus(BaseConstant.BomStatus.PUBLISH.name());
            }
        }
        //先保存所有的节点
        tpBomDAO.save(all);
        //根据code重组关系
        clearRelation(all);
        //保存所有的关系
        tpBomRelationDAO.save(tpBomRelations);
        return all;
    }

    private void clearRelation(List<TPBom> refs) {
        final List<String> codes = QueryHelper.doQueryProperties(refs, true, new QueryHelper.PropertyReader<TPBom, String>() {

            public String read(TPBom tpBom) {
                return tpBom.getCode();
            }
        });
        Specification<TPBomRelation> relationSpecification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TPBomRelation>() {

            public void fillParam(Root<TPBomRelation> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(root.get("ref").get("code").as(String.class).in(codes));
            }
        });
        List<TPBomRelation> relations = tpBomRelationDAO.findAll(relationSpecification);
        tpBomRelationDAO.delete(relations);
    }

    /**
     * 发布一个节点
     *
     * @param id
     */
    public List<TPBom> publish(String id) {
        //检查code是否存在
        TPBom tpBom = findOne(id);
        if (tpBom == null) {
            throw new BusinessException("设计结构不存在，不能发布");
        }
        //递归遍历获取传入的孩子节点
//        找到下属的孩子节点
        tpBom.setNodeLevel(1);
        List<TPBom> tpBoms = reset(tpBom);
        for (TPBom bom : tpBoms) {
            bom.setTpBomRelations(null);
            bom.setStatus(BaseConstant.BomStatus.PUBLISH.name());
        }
        tpBomDAO.save(tpBoms);
        return tpBoms;
    }

    public List<TPBom> reset(TPBom root) {
        List<TPBom> tpBoms = new ArrayList<TPBom>();
        tpBoms.add(root);
        List<TPBom> children = root.getChildren();
        if (ValidatorUtils.isArrayEmpty(children)) {
            return tpBoms;
        }
        for (TPBom tpBom : children) {
            tpBom.setNodeLevel(root.getNodeLevel() + 1);
            tpBoms.addAll(reset(tpBom));
        }
        return tpBoms;
    }

    /**
     * 查询PBOM结构
     *
     * @param searchPBomCondition
     * @param pageable
     * @return
     */
    public Page<TPBom> findPBoms(SearchPBomCondition searchPBomCondition, Pageable pageable) {
        Page<TPBom> page = tpBomDAO.findAll(buildSpecification(searchPBomCondition), pageable);
        return page;
    }

    /**
     * 创建编码辅助
     *
     * @param pName
     * @return
     */
    public String createCode(String pName) {
        String pre = PinyinUtils.getFirstSpell(pName).toUpperCase();
        int i = 1;
        String _pre = pre;
        TPBom tpBom = findByCode(_pre);
        while (tpBom != null) {
            _pre = pre + i;
            tpBom = findByCode(_pre);
            i++;
        }
        return _pre;
    }

    /**
     * 单个结构查询
     *
     * @param id
     * @return
     */
    public TPBom findOne(String id) {
        return StringUtils.isBlank(id) ? null : tpBomDAO.findOne(id);
    }

    /**
     * 查询当前节点的孩子节点
     *
     * @param id
     * @return
     */
    public List<TPBom> findChildren(String id) {
        TPBom tpBom = findOne(id);
        return tpBom.getChildren();
    }

    private TPBom findByCode(String code) {
        SearchPBomCondition searchPBomCondition = new SearchPBomCondition();
        searchPBomCondition.setCode(code);
        List<TPBom> tpBoms = tpBomDAO.findAll(buildSpecification(searchPBomCondition));
        return tpBoms.isEmpty() ? null : tpBoms.get(0);
    }

    private Specification<TPBom> buildSpecification(final SearchPBomCondition searchPBomCondition) {
        Specification<TPBom> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TPBom>() {

            public void fillParam(Root<TPBom> root, CriteriaBuilder cb, List<Predicate> list) {
                if (StringUtils.isNotBlank(searchPBomCondition.getpName())) {
                    list.add(cb.like(root.get("pName").as(String.class), "%" + searchPBomCondition.getpName() + "%"));
                }
                if (StringUtils.isNotBlank(searchPBomCondition.getCode())) {
                    list.add(cb.equal(root.get("code").as(String.class), searchPBomCondition.getCode()));
                }
                if (StringUtils.isNotBlank(searchPBomCondition.getBomType())) {
                    list.add(cb.equal(root.get("bomType").as(String.class), searchPBomCondition.getBomType()));
                }
                if (StringUtils.isNotBlank(searchPBomCondition.getStatus())) {
                    list.add(cb.equal(root.get("status").as(String.class), searchPBomCondition.getStatus()));
                }
                if (StringUtils.isNotBlank(searchPBomCondition.getId())) {
                    list.add(cb.equal(root.get("id").as(String.class), searchPBomCondition.getId()));
                }
                if (ValidatorUtils.isArrayNotEmpty(searchPBomCondition.getIds())) {
                    list.add(root.get("id").as(String.class).in(searchPBomCondition.getIds()));
                }
                if (searchPBomCondition.isRoot()) {
                    CriteriaQuery query = cb.createQuery();
                    Subquery<TPBom> sq = query.subquery(TPBom.class);
                    Root<TPBomRelation> spouseEmp = sq.from(TPBomRelation.class);
                    sq.select(spouseEmp.get("child").as(TPBom.class));
                    list.add(root.in(sq).not());
                }
            }
        });
        return specification;
    }
}
